package me.exchange.com.modules.security.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.exchange.com.annotation.AnonymousAccess;
import me.exchange.com.aop.log.Log;
import me.exchange.com.config.SecurityProperties;
import me.exchange.com.modules.security.TokenProvider;
import me.exchange.com.modules.security.service.OnlineUserService;
import me.exchange.com.modules.security.vo.AuthUser;
import me.exchange.com.modules.security.vo.GoogleAuthUser;
import me.exchange.com.modules.security.vo.JwtUser;
import me.exchange.com.modules.system.domain.UrlConfig;
import me.exchange.com.modules.system.dto.UserDto;
import me.exchange.com.modules.system.service.UrlConfigService;
import me.exchange.com.modules.system.service.UserService;
import me.exchange.com.utils.*;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Zheng Jie
 * @date 2018-11-23
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@Api(tags = "系统：系统授权接口")
public class AuthController {

    @Value("${loginCode.expiration}")
    private Long expiration;
    @Value("${rsa.private_key}")
    private String privateKey;
    @Value("${single.login:false}")
    private Boolean singleLogin;

    @Autowired
    private SecurityProperties properties;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private OnlineUserService onlineUserService;
    @Autowired
    private TokenProvider tokenProvider;
    @Autowired
    private AuthenticationManagerBuilder authenticationManagerBuilder;

    @Autowired
    private UserService userService;

    @Autowired
    private UrlConfigService urlConfigService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request) {
        // 密码解密
        RSA rsa = new RSA(privateKey, null);
        String password = new String(rsa.decrypt(authUser.getPassword(), KeyType.PrivateKey));

        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());

        // 清除验证码
        redisUtils.del(authUser.getUuid());

        if (StringUtils.isBlank(code)) {
            return ResponseEntity.ok(Result.error("验证码不存在或已过期"));
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            return ResponseEntity.ok(Result.error("验证码错误"));
        }

        GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
        //谷歌验证
        Long googleStr = authUser.getGooglecode();
        String secret = authUser.getSecret();
        Long timestamp = authUser.getTimestamp();

        googleAuthenticator.setWindowSize(17);
        boolean checkGoogleCode = googleAuthenticator.check_code(secret,googleStr,timestamp);
        if(!checkGoogleCode) {
            return ResponseEntity.ok(Result.error("谷歌验证失败"));
        }

        UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(authUser.getUsername(),"123456");

        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);

        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUser, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUser);
        }};
        if (singleLogin) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
        }
        return ResponseEntity.ok(Result.ok("登录成功").mergeMap(authInfo));
    }

    @ApiOperation("获取用户信息")
    @PostMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
        return ResponseEntity.ok(Result.ok("获取用户信息成功").put("userInfo", jwtUser));
    }

    @AnonymousAccess
    @ApiOperation("获取验证码")
    @PostMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(2);
        // 获取运算的结果
        String result = captcha.text();
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        // 保存
        redisUtils.set(uuid, result, expiration, TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(Result.ok("获取验证码成功").mergeMap(imgResult));
    }

    @AnonymousAccess
    @ApiOperation("获取遮掩码")
    @PostMapping(value = "/getUrlConfigCode")
    public ResponseEntity<Object> getUrlConfigCode() {

        List<UrlConfig> urlConfigList = urlConfigService.findAllOrderBy();
        if (urlConfigList != null && urlConfigList.size() > 0) {
            Map<String, Object> imgResult = new HashMap<String, Object>(1) {{
                put("webCode", urlConfigList.get(0).getWebcode());
            }};
            return ResponseEntity.ok(Result.ok("获取遮掩码成功").mergeMap(imgResult));
        } else {
            return ResponseEntity.ok(Result.error("获取遮掩码失败"));
        }

    }

    @ApiOperation("退出登录")
    @AnonymousAccess
    @PostMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        onlineUserService.logout(tokenProvider.getToken(request));
        return ResponseEntity.ok(Result.ok("退出登录成功"));
    }

    /**
     * 获取谷歌验证码
     * 0代表已经绑定谷歌验证器
     * 1代表生成谷歌验证码
     * @param googleAuthUser
     * @param request
     * @return
     */
    @Log("用户登录获取谷歌验证码")
    @ApiOperation("用户登录获取谷歌验证码")
    @AnonymousAccess
    @PostMapping(value = "/getGoogleCode")
    public ResponseEntity<Object> getGoogleCode(@Validated @RequestBody GoogleAuthUser googleAuthUser, HttpServletRequest request) {

       String googleSecret = "";
       Map<String, Object> imgResult = new HashMap<String,Object>(16);

        String userName = googleAuthUser.getUsername();

        JwtUser userDetails = (JwtUser) userDetailsService.loadUserByUsername(userName);

        if(userDetails!=null && StringUtils.isNotBlank(userDetails.getGoogleKey())){
            googleSecret = userDetails.getGoogleKey();

            imgResult.put("googleType", "0");

        }else {
            //生成google验证码
            googleSecret = GoogleAuthenticator.generateSecretKey();

            // 把这个qrcode生成二维码，用google身份验证器扫描二维码就能添加成功
            String qrcode = GoogleAuthenticator.getQRBarcode(userName, googleSecret);
            imgResult.put("qrcode",qrcode);
            // 验证码信息
            BufferedImage image = QrCodeUtil.generate(qrcode,new QrConfig());
            //转换成png格式的IO流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                ImageIO.write(image, "jpg", out);
            }catch (Exception e) {
                e.printStackTrace();
            }
            byte[] bytes = out.toByteArray();
//            BASE64Encoder encoder = new BASE64Encoder();
//            String binary = encoder.encodeBuffer(bytes).trim();
            String binary = Base64.encodeBase64String(bytes);

            imgResult.put("img", "qrcode_img:data:image/jpg;base64," + binary);

            imgResult.put("googleType", "1");

        }
        imgResult.put("googleSecret", googleSecret);

        return ResponseEntity.ok(Result.ok("获取验证码成功").mergeMap(imgResult));

    }

      /**
       * 绑定谷歌验证码
       * @param googleAuthUser
       * @return
       */
      @Log("绑定谷歌验证码")
      @ApiOperation("绑定谷歌验证码")
      @AnonymousAccess
      @PostMapping(value = "/checkGoogleCode")
      public ResponseEntity<Object> checkGoogleCode(@Validated @RequestBody GoogleAuthUser googleAuthUser) {

          String userName = googleAuthUser.getUsername();

          UserDto userDto = userService.findByName(userName);
          userDto.setGoogleKey(googleAuthUser.getSecret());

          userService.updateGoogleKey(userDto);

          return ResponseEntity.ok(Result.ok("绑定成功"));


      }

    /**
     * 解绑谷歌验证码
     * @param googleAuthUser
     * @param request
     * @return
     */
    @Log("解绑谷歌验证码")
    @ApiOperation("解绑谷歌验证码")
    @AnonymousAccess
    @PostMapping(value = "/sockGoogleCode")
    public ResponseEntity<Object> sockGoogleCode(@Validated @RequestBody GoogleAuthUser googleAuthUser,HttpServletRequest request) {

        String passwordRsa = googleAuthUser.getPassword();
        if(StringUtils.isBlank(passwordRsa)){
            return ResponseEntity.ok(Result.error("登陆密码不能为空"));
        }

        String userName = googleAuthUser.getUsername();

        UserDto userDto = userService.findByName(userName);
        if(userDto == null){
            return ResponseEntity.ok(Result.error("用户不存在"));
        }
        if(!passwordRsa.equals(userDto.getPassword())){
            return ResponseEntity.ok(Result.error("登陆密码不正确"));
        }
        GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
        //谷歌验证
        Long googleStr = googleAuthUser.getGooglecode();
        String secret = googleAuthUser.getSecret();
        Long timestamp = googleAuthUser.getTimestamp();

        boolean checkGoogleCode = googleAuthenticator.check_code(secret,googleStr,timestamp);
        if(!checkGoogleCode) {
            return ResponseEntity.ok(Result.error("谷歌验证失败"));
        }

        userDto.setGoogleKey("");

        userService.updateGoogleKey(userDto);

        //TODO 预留看解绑之后是否直接推出登陆

        return ResponseEntity.ok(Result.ok("解绑成功"));


    }

    public static void main(String[] args) {
        String pubKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ==";
        RSA rsa = new RSA(null, pubKey);
        String password =rsa.encryptBase64("123456", KeyType.PublicKey);
        System.out.println(password);

        String priKey="MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==";
        RSA rsa2 = new RSA(priKey, null);
        String password2 = new String(rsa2.decrypt(password, KeyType.PrivateKey));
        System.out.println(password2);


    }


}